package com.kay.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description: 添加全局过滤器，进⾏IP注册接⼝的防暴刷控制，超过阈值直接返回错误码及错误信息（错误码：303，错误信息：您频繁进⾏注册，请求已被拒绝）
 * @Author: Kay
 * @CreateDate: 2021/9/28$ 22:11$
 * @UpdateUser: Kay
 * @UpdateDate: 2021/9/28$ 22:11$
 */
@Slf4j
@Component
@RefreshScope
public class IPAntiRiotFilter implements GlobalFilter, Ordered {
    private static Map<String, List<Long>> requestMap = new ConcurrentHashMap<String, List<Long>>();

    @Value("${ip.anti.riot.duration}")
    private Integer duration; // 时间段

    @Value("${ip.anti.riot.frequency}")
    private Integer frequency; // 次数

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("this is IPAntiRiotFilter .................................");
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        // 进⾏IP注册接⼝的防暴刷控制，超过阈值直接返回错误码及错误信息（错误码：303，错误信息：您频繁进⾏注册，请求已被拒绝）
        if( request.getURI().getPath().startsWith("/api/user/register")){
            if (isRequestApproveThreshold(request.getRemoteAddress().getHostString() + request.getPath()))
                return responseResult(response, HttpStatus.SEE_OTHER, "您频繁进⾏注册，请求已被拒绝");
        }

        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }

    private Mono<Void> responseResult(ServerHttpResponse response, HttpStatus httpStatus, String data) {
        response.setStatusCode(httpStatus); // 状态码
        DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
        return response.writeWith(Mono.just(wrap));
    }

    /**
     * 统计当前请求 并判断是否达到防爆刷阈值
     *
     * @param clientIp
     * @return
     */
    private boolean isRequestApproveThreshold(String clientIp) {
        List<Long> requestList = requestMap.get(clientIp);
        if (CollectionUtils.isEmpty(requestList)) requestList = new ArrayList<>();
        requestList.add(System.currentTimeMillis());
        requestMap.put(clientIp, requestList);

        duration = duration == null ? 1 : duration; // 默认为 1分钟
        long durationTime = duration * 60 * 1000;
        long expireTime = System.currentTimeMillis() - durationTime; // 指定时间段之前的毫秒 时间戳

        int result = 0;
        if (requestList != null && requestList.size() > 0) {
            try {
                Iterator<Long> iterator = requestList.iterator();
                while (iterator.hasNext()) {
                    Long time = iterator.next();
                    if (time >= expireTime) {
                        // 时间范围之内
                        result++;
                    } else {
                        // 超过指定时间 被移除
                        iterator.remove();
                    }
                }
            } catch (Exception e) {

            }

        }
        frequency = frequency == null ? 100 : frequency; // 默认为100次
        return result > frequency ? true : false;
    }
}
